Перейти к основному содержимому

5.15. Управляющие конструкции

Разработчику Архитектору

Управляющие конструкции

Операторы
Условия
Циклы
Итерируемые таблицы

Lua поддерживает три базовых логических оператора: and, or, not. Их поведение следует парадигме короткого замыкания (short-circuit evaluation) и тесно связано с концепцией «истинности» значений в условиях отсутствия строгой булевой типизации. Ранее мы уже вкратце упомянули о том, что они заменяют собой некоторые символьные операторы, чем и упрощают язык, ведь мы словно пишем на английском языке.

not — унарный оператор, возвращающий true, если операнд является nil или false, и false — во всех остальных случаях. Результат всегда имеет тип boolean.

not nil     --> true
not false --> true
not 0 --> false (0 считается истинным!)
not "" --> false

and — бинарный оператор, возвращающий первый аргумент, если он ложный (nil или false), иначе — второй. Не приводит результат к булеву типу, а возвращает фактическое значение последнего вычисленного операнда.

true and 42     --> 42
false and 42 --> false
nil and "hello" --> nil

or — возвращает первый истинный операнд или последний, если все ложны. Также не производит приведение типов.

nil or "default"     --> "default"
false or 0 --> 0 (0 — истинно!)
"a" or "b" --> "a"

В Lua всё, кроме nil и false, считается истинным (truthy). Это включает числа (даже ноль), строки (даже пустые), таблицы, функции и т.д. Данная особенность позволяет использовать выражения как идиому для установки значения по умолчанию:

local value = input or "default"

Таким образом, операторы and и or являются не только логическими, но и средствами управления потоком данных, что характерно для функциональных и скриптовых парадигм.

Lua предоставляет две формы условной конструкции: if-then-else и её компактный аналог через and/or, хотя последний используется с осторожностью из-за семантических различий. Синтаксис if-then-else следующий:

if condition1 then
-- блок 1
elseif condition2 then
-- блок 2
else
-- блок 3
end

Условие (condition) интерпретируется как булево выражение согласно правилу truthiness.

Конструкция elseif не является макросом или сахаром — это часть синтаксиса, эквивалентная вложенному if, но более читаемая.

Блоки выполняются последовательно; после первого истинного условия остальные игнорируются.

Вложенность допускается без ограничений.

Пример:

if x > 0 then
print("positive")
elseif x < 0 then
print("negative")
else
print("zero")
end

Важно отметить, что в отличие от C-подобных языков, в Lua отсутствует тернарный оператор ?:. Однако его можно эмулировать через and/or, при соблюдении осторожности:

local result = (a > b) and x or y  -- работает, если x не ложно

Однако если x == false или x == nil, выражение вернёт y, что может быть ошибкой. Поэтому рекомендуется использовать полноценный if.

Теперь поговорим о циклах.

Lua поддерживает три типа циклов: while, repeat-until и for (с двумя формами).

Цикл while выполняет тело, пока условие истинно. Проверка происходит до каждой итерации. Если условие ложно изначально, тело не выполнится ни разу.

while condition do
-- тело цикла
end

Цикл repeat-until представляет собой аналог do-while в других языках. Тело выполняется хотя бы один раз, проверка — в конце.

repeat
-- тело цикла
until condition -- выход при истине условия

Обратите внимание: until завершает цикл, когда условие становится истинным, что противоположно семантике while.

Цикл for бывает в двух формах - числовой и итерационный.

Числовой for используется для перебора диапазона целых чисел.

for var = start, stop, step do
-- тело
end

step по умолчанию равен 1.

Все три параметра вычисляются единожды перед началом цикла.

Переменная var локальна по отношению к циклу (это важно!).

for i = 1, 10, 2 do
print(i) -- 1, 3, 5, 7, 9
end

Итерационный for (generic for) предназначен для обхода коллекций с помощью итераторов. Наиболее часто используется с таблицами.

for key, value in iterator, table, start_state do
-- тело
end

На практике применяются стандартные итераторы:

  • pairs(tbl) — перебирает все элементы таблицы (в произвольном порядке).
  • ipairs(tbl) — перебирает элементы с целочисленными ключами от 1 до первого nil.
for k, v in pairs(mytable) do
print(k, v)
end

Использование pairs и ipairs требует понимания различий: pairs негарантированный, а ipairs последовательный.

Механизм generic for основан на протоколе итераций: итератор — это функция, возвращающая на каждом шаге новые значения ключа/значения, пока не вернёт nil.

t = {10, 20, nil, 40, [5] = 50, name = "Alice"}

for i, v in ipairs(t) do
print(i, v) -- 1:10, 2:20 (остановка на nil)
end

for k, v in pairs(t) do
print(k, v) -- может напечатать 1,2,5,"name" в любом порядке
end

При модификации таблицы во время итерации поведение pairs и ipairs не определено. Lua не гарантирует безопасность итерации при изменении структуры.